package ltd.bugs.cute.httpclient;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;

import javax.net.ssl.SSLException;

import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.protocol.HttpContext;

import ltd.bugs.cute.common.spring.ApplicationContextHolder;
import ltd.bugs.cute.httpclient.config.HttpClientConfig;

/**
 * 失败重试的builder
 * 
 * @author zhengzhq
 *
 */
public class HttpRequestRetryHandlerBuilder {

  private static HttpClientConfig httpClientConfig =
      ApplicationContextHolder.getBean(HttpClientConfig.class);

  public static HttpRequestRetryHandler build() {
    return new HttpRequestRetryHandler() {
      @Override
      public boolean retryRequest(IOException exception, int executionCount,
          HttpContext httpContext) {
        if (executionCount >= httpClientConfig.getRetryTimes()) {
          return false;
        }
        // In some circumstances, usually when under heavy load, the web server may be able to
        // receive requests but unable to process them. A lack of sufficient resources like worker
        // threads is a good example. This may cause the server to drop the connection to the client
        // without giving any response. HttpClient throws NoHttpResponseException when it encounters
        // such a condition. In most cases it is safe to retry a method that failed with
        // NoHttpResponseException.
        if (exception instanceof NoHttpResponseException) {
          return true;
        }

        if (exception instanceof InterruptedIOException) {
          // Timeout
          return false;
        }

        if (exception instanceof UnknownHostException) {
          // Unknown host
          return false;
        }

        // 网络超时暂时不重试，避免上游服务堵塞等待时间长
        if (exception instanceof ConnectTimeoutException) {
          // Connection refused
          return false;
        }

        if (exception instanceof SSLException) {
          // SSL handshake exception
          return false;
        }
        HttpClientContext clientContext = HttpClientContext.adapt(httpContext);
        HttpRequest request = clientContext.getRequest();
        boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
        if (idempotent) {
          // Retry if the request is considered idempotent
          return true;
        }

        return false;
      }
    };
  }
}
